﻿namespace FileTransfer.Client.Model;

public class ProgressableReadStream : Stream
{
    private readonly Stream _baseStream;
    private readonly Action<long, long> _progressCallback;
    private readonly long _totalLength;
    private long _bytesRead;

    public ProgressableReadStream(
        Stream baseStream,
        long totalLength,
        Action<long, long> progressCallback
    )
    {
        _baseStream = baseStream ?? throw new ArgumentNullException(nameof(baseStream));
        _totalLength = totalLength;
        _progressCallback =
            progressCallback ?? throw new ArgumentNullException(nameof(progressCallback));
    }

    public override bool CanRead => _baseStream.CanRead;
    public override bool CanSeek => _baseStream.CanSeek;
    public override bool CanWrite => _baseStream.CanWrite;
    public override long Length => _baseStream.Length;

    public override long Position
    {
        get => _baseStream.Position;
        set => _baseStream.Position = value;
    }

    public override void Flush() => _baseStream.Flush();

    public override async Task FlushAsync(CancellationToken cancellationToken) =>
        await _baseStream.FlushAsync(cancellationToken);

    public override int Read(byte[] buffer, int offset, int count)
    {
        int bytesRead = _baseStream.Read(buffer, offset, count);
        if (bytesRead > 0)
        {
            ReportProgress(bytesRead);
        }
        return bytesRead;
    }

    public override async Task<int> ReadAsync(
        byte[] buffer,
        int offset,
        int count,
        CancellationToken cancellationToken
    )
    {
        int bytesRead = await _baseStream.ReadAsync(
            buffer.AsMemory(offset, count),
            cancellationToken
        );
        if (bytesRead > 0)
        {
            ReportProgress(bytesRead);
        }
        return bytesRead;
    }

    public override long Seek(long offset, SeekOrigin origin) => _baseStream.Seek(offset, origin);

    public override void SetLength(long value) => _baseStream.SetLength(value);

    public override void Write(byte[] buffer, int offset, int count) =>
        _baseStream.Write(buffer, offset, count);

    private void ReportProgress(int bytesReadThisCall)
    {
        _bytesRead += bytesReadThisCall;
        _progressCallback(_bytesRead, _totalLength);
    }

    public override void Close() => _baseStream.Close();

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _baseStream.Dispose();
        }
        base.Dispose(disposing);
    }
}